home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1992 June: ROMin Holiday / ADC Developer CD (1992-06) (''ROMin Holiday'')_iso / Developer Connection - 06-1992.iso / Developer Essentials / DTS Sample Code / System 7.0 Samples / ProcDoggie 1.0a6⁄THINK P / UProcessUtils.p < prev   
Encoding:
Text File  |  1991-02-21  |  44.8 KB  |  1,174 lines  |  [TEXT/PJMM]

  1. unit UProcessUtils;
  2.  
  3. {-------------------------------------------------------------------------------}
  4. {#}
  5. {#    Apple Macintosh Developer Technical Support}
  6. {#}
  7. {#    Interfaces for the guts of the ProcDoggie application}
  8. {#}
  9. {#    Program:    ProcDoggie}
  10. {#    File:        UProcessUtils.p - Pascal Implementation}
  11. {#}
  12. {#    by:        Forrest Tanaka}
  13. {#}
  14. {#    Copyright © 1988-1991 Apple Computer, Inc.}
  15. {#    All rights reserved.}
  16. {#}
  17. {--------------------------------------------------------------------------------}
  18. {#}
  19. {#    This unit is a high-level interface to the Process Manager.  It contains}
  20. {#    routines which allow you to launch a process, terminate a process, count the}
  21. {#    number of open processes, find a process given a file specification, and}
  22. {#    manage a list of documents for a process to open or print when it is}
  23. {#    launched.}
  24. {#}
  25. {#    The LaunchProcess routine is the most important routine in this unit.  It}
  26. {#    lets you launch either an application or a desk accessory and optionally lets}
  27. {#    you pass it a list of documents for the launched application to open or print}
  28. {#    (desk accessories don’t have document lists).}
  29. {#}
  30. {-------------------------------------------------------------------------------}
  31. {[j=20/57/1$] Pasmat Options}
  32.  
  33. {-------------------------------------------------------------------------------}
  34. {#}
  35. {#     2/21/91 pvh - THINK Pascal conversion.}
  36. {#    Notes:}
  37. {#}
  38. {#}
  39. {-------------------------------------------------------------------------------}
  40.  
  41. interface
  42.  
  43.  
  44. (*******************************************************************************}
  45. {* Used Units}
  46. {*******************************************************************************)
  47.  
  48.     uses
  49.         (* Group 1 *)
  50.         Types, QuickDraw, 
  51.  
  52.         (* Group 2 *)
  53.         AppleTalk, PPCToolBox, OSUtils, Files, Processes, EPPC, Notification, AppleEvents, Errors, Events, Memory, Resources, SegLoad, 
  54.  
  55.         (* Group 3 *)
  56.         Aliases;
  57.  
  58.  
  59. (*******************************************************************************}
  60. {* Types}
  61. {*******************************************************************************)
  62.  
  63.     type
  64.         (* Flag to indicate whether document list is for printing or opening *)
  65.         LaunchModeCode = (kJustLaunch, kOpenLaunch, kPrintLaunch);
  66.  
  67.         (* Document list entry for opening apps with docs to open or print *)
  68.         DocListEntryPtr = ^DocListEntryRec;
  69.         DocListEntryHnd = ^DocListEntryPtr;
  70.         DocListEntryRec = record
  71.                 next: DocListEntryHnd; {Link to the next document list record}
  72.                 docFile: FSSpec           {File specification}
  73.             end;
  74.  
  75.         (* Document list for opening apps with docs to open or print *)
  76.         DocListRec = record
  77.                 docList: DocListEntryHnd; {Handle to the first document list entry}
  78.                 openPrint: LaunchModeCode   {Opening or printing documents?}
  79.             end;
  80.         DocListPtr = ^DocListRec;
  81.         DocListHnd = ^DocListPtr;
  82.  
  83.  
  84. (*******************************************************************************}
  85. {* CreateDocList - Create a new document list}
  86. {*}
  87. {* This routine creates a new document list and returns a handle to it.  If there}
  88. {* isn’t enough memory for a new document list, CreateDocList returns NIL.}
  89. {*}
  90. {* If openOrPrint is kOpenLaunch, then the list of documents that are put into}
  91. {* the new document list will be opened when the application is launched.  If}
  92. {* openOrPrint is kPrintLaunch, then the list of documents will be printed when}
  93. {* the application is launched.  If kJustLaunch or any other value is passed in}
  94. {* openOrPrint, then no document list is created.  CreateDocList returns NIL in}
  95. {* this case.  This isn’t really a conflict with returning NIL when there’s not}
  96. {* enough memory for the new document list because the calling routine can check}
  97. {* to see if openOrPrint is valid or not itself.}
  98. {*******************************************************************************)
  99.  
  100.     function CreateDocList (openOrPrint: LaunchModeCode): DocListHnd;
  101.  
  102.  
  103. (*******************************************************************************}
  104. {* IsEmptyDocList - Return TRUE if the specified document list is empty.}
  105. {*}
  106. {* IsEmptyDocList returns TRUE if the document list specified by "theList" is}
  107. {* empty.  “Empty” can mean either that theList is NIL or theList is a handle to}
  108. {* a document list that contains no documents.  It returns FALSE if there’s at}
  109. {* least one entry in "theList".}
  110. {*}
  111. {* theList must either be a valid handle to a document list or it must be NIL.}
  112. {* IsEmptyDocList is unpredictable if this isn’t true.}
  113. {*******************************************************************************)
  114.  
  115.     function IsEmptyDocList (theList: DocListHnd): Boolean;
  116.  
  117.  
  118. (*******************************************************************************}
  119. {* AddToDocList - Add a document file specification to a document list}
  120. {*}
  121. {* AddToDocList adds the file specified by "newFile" to the end of the document}
  122. {* list specified by "theList".  If there isn’t enough memory to add a new file}
  123. {* to the document list, then AddToDocList returns memFullErr.  Otherwise, noErr}
  124. {* is returned.}
  125. {*}
  126. {* theList MUST be a valid handle to an initialized document list.  I won’t be}
  127. {* responsible for AddToDocList’s actions if this isn’t true.}
  128. {*******************************************************************************)
  129.  
  130.     function AddToDocList (newFile: FSSpec; theList: DocListHnd): OSErr;
  131.  
  132.  
  133. (*******************************************************************************}
  134. {* DisposeDocList - Dispose of a document list}
  135. {*}
  136. {* DisposeDocList deallocates all the memory occupied by the document list}
  137. {* specified by the "theList" parameter.  If theList is NIL, then nothing is}
  138. {* done.  If it’s not a valid DocListHnd, DisposeDocList is unpredictable.}
  139. {*******************************************************************************)
  140.  
  141.     procedure DisposeDocList (theList: DocListHnd);
  142.  
  143.  
  144. (*******************************************************************************}
  145. {* WereInFront - Test to see if this application is in front or not}
  146. {*}
  147. {* This routine determines whether this application is the front-most application}
  148. {* or not.  If it is, then TRUE is returned.  If it isn’t, then FALSE is}
  149. {* returned.}
  150. {*******************************************************************************)
  151.  
  152.     function WereInFront: Boolean;
  153.  
  154.  
  155. (*******************************************************************************}
  156. {* FindProcess - Find a process with specified file and name}
  157. {*}
  158. {* FindProcess searches the Process Manager’s process list for a process that was}
  159. {* launched from the file specified by "testFile".  If the process being searched}
  160. {* is a DA, then the "daName" parameter must specify the name of the desk}
  161. {* accessory (i.e. the name of the DRVR resource, including the initial null}
  162. {* character).  If the process is found, then information about the process is}
  163. {* returned in the "testFileInfo" parameter, and FindProcess returns TRUE.  If}
  164. {* the process could not be found, then FALSE is returned, and "testFileInfo" is}
  165. {* unchanged.}
  166. {*}
  167. {* Warning: the processName and processAppSpec fields of "testFileInfo" are NOT}
  168. {* valid when FindProcess returns TRUE.  If the name and FSSpec of the found file}
  169. {* are desired, then the calling routine must allocate space for those fields}
  170. {* and call GetProcessInfo itself, passing the ProcessInfoRec returned by this}
  171. {* routine as a parameter.}
  172. {*******************************************************************************)
  173.  
  174.     function FindProcess (testFile: FSSpec; daName: StringPtr; var testFileInfo: ProcessInfoRec): Boolean;
  175.  
  176.  
  177. (*******************************************************************************}
  178. {* LaunchProcess - Launch the specified process}
  179. {*}
  180. {* This routine launches the process whose file has the location and name}
  181. {* specified by "processFile" parameter.  The process’s resulting serial number}
  182. {* is returned in the returnPSN parameter.  If the process to be launched is a}
  183. {* desk accessory, then the name of the desk accessory (including the initial}
  184. {* null character that desk accessories require) is passed in daName.  IF NIL is}
  185. {* passed in daName, then the first desk accessory found in the specified file}
  186. {* (according to the Resource Manager) is launched.  If an application is being}
  187. {* launched, then daName is ignored.  The options parameter specifies options to}
  188. {* use when launching the new process.  It has the same values and meanings as}
  189. {* the LaunchFlags type defined in the Process Manager chapter of Inside}
  190. {* Macintosh VI.  If a desk accessory is being launched, then only the}
  191. {* launchContinue flag has significance.}
  192. {*}
  193. {* A list of documents to be opened or printed by the launched application can}
  194. {* optionally be specified by the docList parameter.  If no documents are}
  195. {* specified, then docList should be NIL.  See the document list routines defined}
  196. {* early in this unit.}
  197. {*}
  198. {* LaunchProcess determines whether to launch an application or desk accessory}
  199. {* based on the type code of the file specified by processFile.  If the file’s}
  200. {* type is APPL, then LaunchProcess attempts to launch it as an application.  If}
  201. {* the file has any other type, then LaunchProcess attempts to launch a desk}
  202. {* accessory in that file.}
  203. {*}
  204. {* LaunchProcess returns two kinds of errors.  One error is returned as a}
  205. {* function result.  These kinds of errors are generated by any call that occurs}
  206. {* during the execution of LaunchProcess that is only used to manage the}
  207. {* launching of the specified application rather than launching the application}
  208. {* itself.   The launchError parameter returns the error code of any error that}
  209. {* occurs when the application is actually launched.}
  210. {*******************************************************************************)
  211.  
  212.     function LaunchProcess (processFile: FSSpec; daName: StringPtr; docList: DocListHnd; options: LaunchFlags; var returnPSN: ProcessSerialNumber; var launchError: OSErr): OSErr;
  213.  
  214.  
  215. (*******************************************************************************}
  216. {* CountProcesses - Count the number of open processes}
  217. {*}
  218. {* This routine searches through the Process Manager’s process list and counts}
  219. {* the number of open processes.  The result is returned.}
  220. {*******************************************************************************)
  221.  
  222.     function CountProcesses: Integer;
  223.  
  224.  
  225. (*******************************************************************************}
  226. {* TerminateProcess - Terminate a process}
  227. {*}
  228. {* This routine causes the process specified by "theProcessNum" to be terminated.}
  229. {* If an error occurs, then the error code is returned.}
  230. {*******************************************************************************)
  231.  
  232.     function TerminateProcess (theProcessNum: ProcessSerialNumber): OSErr;
  233.  
  234.  
  235. implementation
  236.  
  237.     const
  238.         kCaseSens = TRUE; {Pass to EqualString for case-sensitive check}
  239.         kDiacSens = TRUE; {Pass to EqualString for diacritical-sensitive check}
  240.  
  241.  
  242. {$S ProcessUtils}
  243. (*******************************************************************************}
  244. {* Private: EqualFSSpec - Check equality of FSSpec records}
  245. {*}
  246. {* EqualFSSpec returns TRUE if the file specified by spec1 refers to the same}
  247. {* file as the one specified by spec2.  Otherwise, EqualFSSpec returns FALSE.}
  248. {*}
  249. {* To compare names, I’m using EqualString with no case sensitivity, but with}
  250. {* sensitivity to diacriticals.  This isn’t usually the recommended way of}
  251. {* comparing strings, because the Script Manager has routines for comparing}
  252. {* strings in a more sophisticated, localizable way.  But the File Manager uses}
  253. {* _CmpString, which is the assembly language equivalent of EqualString, so}
  254. {* that’s the way I must do things here.}
  255. {*******************************************************************************)
  256.  
  257.     function EqualFSSpec (spec1: FSSpec; spec2: FSSpec): Boolean;
  258.  
  259.     begin
  260.         EqualFSSpec := (spec1.vRefNum = spec2.vRefNum) and (spec1.parID = spec2.parID) and (EqualString(spec1.name, spec2.name, not kCaseSens, kDiacSens))
  261.     end;
  262.  
  263.  
  264. {$S ProcessUtils}
  265. (*******************************************************************************}
  266. {* Public: CreateDocList}
  267. {*}
  268. {* A document list record is allocated and initialized.  Pretty simple.}
  269. {*******************************************************************************)
  270.  
  271.     function CreateDocList (openOrPrint: LaunchModeCode): DocListHnd;
  272.  
  273.         var
  274.             newList: DocListHnd; {Handle to the new DocListRec}
  275.  
  276.     begin
  277.         if (openOrPrint = kOpenLaunch) or (openOrPrint = kPrintLaunch) then
  278.             begin
  279.                 newList := DocListHnd(NewHandle(SIZEOF(DocListRec)));
  280.                 if newList <> nil then
  281.                     begin
  282.                         newList^^.docList := nil;
  283.                         newList^^.openPrint := openOrPrint
  284.                     end;
  285.                 CreateDocList := newList
  286.             end
  287.     end;
  288.  
  289.  
  290. {$S ProcessUtils}
  291. (*******************************************************************************}
  292. {* Public: IsEmptyDocList}
  293. {*}
  294. {* If the document list record is NIL, or if the docList field is NIL, then the}
  295. {* given document list is empty.}
  296. {*******************************************************************************)
  297.  
  298.     function IsEmptyDocList (theList: DocListHnd): Boolean;
  299.  
  300.     begin
  301.         IsEmptyDocList := (theList = nil) | (theList^^.docList = nil)
  302.     end;
  303.  
  304.  
  305. {$S ProcessUtils}
  306. (*******************************************************************************}
  307. {* Public: AddToDocList}
  308. {*}
  309. {* A new document list entry is allocated and then "newFile" is copied into its}
  310. {* docFile field.  It is then added to end of "theList".}
  311. {*******************************************************************************)
  312.  
  313.     function AddToDocList (newFile: FSSpec; theList: DocListHnd): OSErr;
  314.  
  315.         var
  316.             newEntry: DocListEntryHnd; {Handle to the new DocListEntryRec}
  317.             currEntry: DocListEntryHnd; {Handle to DocListEntryRec being checked}
  318.  
  319.     begin
  320.         (* Create a new document list entry *)
  321.         newEntry := DocListEntryHnd(NewHandle(SIZEOF(DocListEntryRec)));
  322.         if newEntry <> nil then
  323.             begin
  324.                 (* Initialize the new document list entry *)
  325.                 newEntry^^.next := nil;
  326.                 newEntry^^.docFile := newFile;
  327.  
  328.                 if IsEmptyDocList(theList) then
  329.                     theList^^.docList := newEntry
  330.                 else
  331.                     begin
  332.                         (* Start from the head of the list *)
  333.                         currEntry := theList^^.docList;
  334.  
  335.                         (* Find the end of the list *)
  336.                         while currEntry^^.next <> nil do
  337.                             currEntry := currEntry^^.next;
  338.  
  339.                         (* Attach the new entry to the end of the list *)
  340.                         currEntry^^.next := newEntry
  341.                     end;
  342.                 AddToDocList := noErr
  343.             end
  344.         else
  345.             AddToDocList := MemError
  346.     end;
  347.  
  348.  
  349. {$S ProcessUtils}
  350. (*******************************************************************************}
  351. {* Private: DisposeAllDocEntries - Dispose of all document list entries}
  352. {*}
  353. {* Ahhh. . . good old, basic, text-book recursion.  This routine has a very small}
  354. {* stack frame, so the recursion isn’t very expensive in this case.  Document}
  355. {* lists are normally very short too.}
  356. {*}
  357. {* This routine disposes of every document list entry in a linked list of}
  358. {* document list entries starting from the entry docListEntry.  If docListEntry}
  359. {* isn’t NIL and it isn’t a valid DocListEntryHnd, DisposeAllDocEntries probably}
  360. {* blows up in very bad ways.}
  361. {*******************************************************************************)
  362.  
  363.     procedure DisposeAllDocEntries (docListEntry: DocListEntryHnd);
  364.  
  365.     begin
  366.         if docListEntry <> nil then
  367.             begin
  368.                 if docListEntry^^.next <> nil then
  369.                     DisposeAllDocEntries(docListEntry^^.next);
  370.                 DisposHandle(Handle(docListEntry))
  371.             end
  372.     end;
  373.  
  374.  
  375. {$S ProcessUtils}
  376. (*******************************************************************************}
  377. {* Public: DisposeDocList}
  378. {*}
  379. {* Should be pretty obvious how this works.}
  380. {*******************************************************************************)
  381.  
  382.     procedure DisposeDocList (theList: DocListHnd);
  383.  
  384.     begin
  385.         if theList <> nil then
  386.             begin
  387.                 (* Dispose of all document list entries *)
  388.                 DisposeAllDocEntries(theList^^.docList);
  389.  
  390.                 (* Dispose of the document list record *)
  391.                 DisposHandle(Handle(theList))
  392.             end
  393.     end;
  394.  
  395.  
  396. {$S ProcessUtils}
  397. (*******************************************************************************}
  398. {* Public: WereInFront}
  399. {*}
  400. {* The current application’s process serial number can always be represented by}
  401. {* setting the high long integer to 0 and setting the low long integer to the}
  402. {* constant kCurrentProcess.  This process serial number is compared against the}
  403. {* process serial number of the front application.  If they’re the same, then}
  404. {* this application is the front application.}
  405. {*******************************************************************************)
  406.  
  407.     function WereInFront: Boolean;
  408.  
  409.         var
  410.             thisProcess: ProcessSerialNumber; {Current process’s PSN}
  411.             frontProcess: ProcessSerialNumber; {Front process’s PSN}
  412.             inFront: Boolean;             {TRUE if we’re in front}
  413.             error: OSErr;
  414.  
  415.         procedure RecoverError (errorCode: OSErr);
  416.  
  417.         begin
  418.             WereInFront := TRUE;
  419.             EXIT(WereInFront)
  420.         end;
  421.  
  422.     begin
  423.         (* Make a PSN for this application *)
  424.         thisProcess.highLongOfPSN := 0;
  425.         thisProcess.lowLongOfPSN := kCurrentProcess;
  426.  
  427.         (* Get the PSN of the front process *)
  428.         error := GetFrontProcess(frontProcess);(*<*)
  429.         if error <> noErr then
  430.             RecoverError(error);
  431.  
  432.         (* See if this application is the front process *)
  433.         error := SameProcess(thisProcess, frontProcess, inFront); (*<*)
  434.         if error <> noErr then
  435.             RecoverError(error);
  436.  
  437.         WereInFront := inFront
  438.     end;
  439.  
  440.  
  441. {$S ProcessUtils}
  442. (*******************************************************************************}
  443. {* Private: CreateDocListDesc - Create an AE descriptor for a list of documents}
  444. {*}
  445. {* This routine takes a document list in the "docList" parameter and converts it}
  446. {* to an 'appa' (typeAppParameters) AppleEvent descriptor containing a list of}
  447. {* documents to be opened by the Process Manager’s LaunchApplication routine.}
  448. {* This descriptor is returned.}
  449. {*}
  450. {* To do this, we have to first build an AppleEvent for an 'odoc'}
  451. {* (kAEOpenDocuments) or a 'pdoc' (kAEPrintDocuments) event that contains the}
  452. {* same list of documents that are contained in the "theDocList" parameter.  Once}
  453. {* that’s done, this AppleEvent is coerced into an typeAppParameters descriptor.}
  454. {*}
  455. {* To create the kAEOpenDocuments or kAEPrintDocuments AppleEvent, a target}
  456. {* address descriptor is needed.  Because we’re converting the AppleEvent into a}
  457. {* typeAppParameters descriptor rather than sending it somewhere, it doesn’t}
  458. {* matter what target address we use.  I just used the process serial number}
  459. {* (PSN) of this application as a dummy value.  Because this is an application}
  460. {* and not something like a driver, I can just use the "kCurrentProcess" constant}
  461. {* to represent this application’s PSN.  A descriptor is made of this PSN, then}
  462. {* this PSN is used when creating the new kAEOpenDocuments AppleEvent.  Once this}
  463. {* AppleEvent is created, the PSN descriptor is no longer needed and is disposed}
  464. {* of.}
  465. {*}
  466. {* kAEOpenDocuments AppleEvents needs a list of file aliases; each file alias}
  467. {* represents a document to open.  To do this, a new descriptor list is created,}
  468. {* and an alias record is created.  Then, each document in the "theDocList"}
  469. {* parameter is converted to an alias in the alias record, then the alias record}
  470. {* is manually converted into an 'alis' (typeAlias) descriptor.  I did this}
  471. {* manually because an alias is already a handle, and it didn’t seem necessary to}
  472. {* go through the extra expense of the AECreateDesc routine.  This typeAlias}
  473. {* descriptor is then added to the descriptor list we created earlier in this}
  474. {* paragraph.  Once the entire document list is converted to the descriptor list,}
  475. {* the alias record is no longer needed and is disposed of.}
  476. {*}
  477. {* This descriptor list full of document aliases is then added to the}
  478. {* kAEOpenDocuments AppleEvent I created.  Because the descriptor list is copied}
  479. {* into the AppleEvent, it is disposed of after the AEPutParamDesc call.}
  480. {*}
  481. {* Then, the most crucial call is made, AECoerceDesc.  This routine coerces the}
  482. {* AppleEvent we just made into a typeAppParameters descriptor.  The record}
  483. {* specified by the dataHandle of this descriptor is exactly in the same format}
  484. {* as the AppParameters record defined in the “Launching Other Applications”}
  485. {* section of the Process Manager chapter of Inside Macintosh VI.}
  486. {*}
  487. {* You can HLock the dataHandle, then set the "launchAppParameters" field of the}
  488. {* LaunchParamBlockRec to the dereferenced dataHandle.  LaunchApplication can}
  489. {* then launch the specified application with the document list that was created.}
  490. {* If the launched application is high-level event aware (as specified in its}
  491. {* SIZE resource), the Process Manager converts LaunchAppParameters back to a}
  492. {* real high-level event which is processed by the application to (hopefully)}
  493. {* open the documents.  Remember that LaunchAppParameters can be any high-level}
  494. {* event, but it’ll usually be used to pass an 'odoc' or 'pdoc' event, and that’s}
  495. {* what we’re using here.  If the launched application isn’t high-level event}
  496. {* aware, and if LaunchAppParameters is an 'odoc' or 'pdoc' event, then the}
  497. {* Process Manager will convert the 'odoc' or 'pdoc' event into the old-style}
  498. {* application parameters so that an old-style application can still open}
  499. {* documents properly.}
  500. {*}
  501. {* This routine is important.  Read it.  Understand it.  Then teach me how it}
  502. {* works.}
  503. {*******************************************************************************)
  504.  
  505.     function CreateDocListDesc (theDocList: DocListHnd; var launchDesc: AEDesc): OSErr;
  506.  
  507.         const
  508.             kPutAtEnd = 0; {For AEPutDesc; put new descriptor at end of list}
  509.  
  510.         var
  511.             applMessage: AppleEvent;          {odoc/pdoc event w/ spec’d doc list}
  512.             docDescList: AEDescList;          {Desc list of doc alias descs}
  513.             selfPSN: ProcessSerialNumber; {Our own PSN; not really used here}
  514.             selfAddress: AEDesc;              {Desc for our own PSN; ditto}
  515.             docDesc: AEDesc;              {Desc for doc alias}
  516.             docAlias: AliasHandle;         {Alias for specified docs}
  517.             currDocEntry: DocListEntryHnd;     {Handle to current doc list entry}
  518.             openPrintCmd: AEEventID;           {odoc or pdoc event?}
  519.             wasChanged: Boolean;             {TRUE if UpdateAlias changed alias}
  520.             error: OSErr;
  521.  
  522.         procedure RecoverError (errorCode: Integer);
  523.  
  524.         begin
  525.             if selfAddress.dataHandle <> nil then
  526.                 error := AEDisposeDesc(selfAddress);(*◊*)
  527.             if applMessage.dataHandle <> nil then
  528.                 error := AEDisposeDesc(applMessage);(*◊*)
  529.             if docDescList.dataHandle <> nil then
  530.                 error := AEDisposeDesc(docDescList);(*◊*)
  531.             if docAlias <> nil then
  532.                 DisposHandle(Handle(docAlias));
  533.             CreateDocListDesc := errorCode;
  534.             EXIT(CreateDocListDesc)
  535.         end;
  536.  
  537.     begin
  538.         selfAddress.dataHandle := nil;
  539.         applMessage.dataHandle := nil;
  540.         docDescList.dataHandle := nil;
  541.         docAlias := nil;
  542.  
  543.         (* Make descriptor for my own PSN; just for AECreateAppleEvent’s yuks *)
  544.         selfPSN.highLongOfPSN := 0;
  545.         selfPSN.lowLongOfPSN := kCurrentProcess;
  546.         error := AECreateDesc(typeProcessSerialNumber, @selfPSN, SIZEOF(ProcessSerialNumber), selfAddress); (*<*)
  547.         if error <> noErr then
  548.             RecoverError(error);
  549.  
  550.         (* Create an AppleEvent for our list of document descriptors *)
  551.         if theDocList^^.openPrint = kOpenLaunch then
  552.             openPrintCmd := kAEOpenDocuments
  553.         else if theDocList^^.openPrint = kPrintLaunch then
  554.             openPrintCmd := kAEPrintDocuments;
  555.         error := AECreateAppleEvent(kCoreEventClass, openPrintCmd, selfAddress, kAutoGenerateReturnID, kAnyTransactionID, applMessage); (*<*)
  556.         if error <> noErr then
  557.             RecoverError(error);
  558.  
  559.         (* PSN copied into the odoc or pdoc event, so don’t need it anymore *)
  560.         error := AEDisposeDesc(selfAddress);(*◊*)
  561.  
  562.         (* Create list of descriptors for files; don’t use list factorization *)
  563.         error := AECreateList(nil, 0, FALSE, docDescList); (*<*)
  564.         if error <> noErr then
  565.             RecoverError(error);
  566.  
  567.         (* Create an alias for the first document *)
  568.         currDocEntry := theDocList^^.docList;
  569.         HLock(Handle(currDocEntry));
  570.         error := NewAlias(nil, currDocEntry^^.docFile, docAlias); (*<*)
  571.         HUnlock(Handle(currDocEntry));
  572.         if error <> noErr then
  573.             RecoverError(error);
  574.  
  575.         (* Put each document in the document list into document descriptor list *)
  576.         while currDocEntry <> nil do
  577.             begin
  578.                 (* Convert alias into an alias descriptor manually *)
  579.                 docDesc.descriptorType := typeAlias;
  580.                 docDesc.dataHandle := Handle(docAlias);
  581.  
  582.                 (* Put the alias descriptor into the document descriptor list *)
  583.                 error := AEPutDesc(docDescList, kPutAtEnd, docDesc);(*◊*)
  584.                 if error <> noErr then
  585.                     RecoverError(error);
  586.  
  587.                 (* Go to the next document in the document list *)
  588.                 currDocEntry := currDocEntry^^.next;
  589.  
  590.                 (* Convert the next document’s FSSpec into the alias *)
  591.                 if currDocEntry <> nil then
  592.                     begin
  593.                         HLock(Handle(currDocEntry));
  594.                         error := UpdateAlias(nil, currDocEntry^^.docFile, docAlias, wasChanged);
  595.                                 (*<*)
  596.  (*<*)
  597.                         HUnlock(Handle(currDocEntry));
  598.                         if error <> noErr then
  599.                             RecoverError(error)
  600.                     end
  601.             end;
  602.  
  603.         (* All aliases are in docDescList, so don’t need the alias record *)
  604.         DisposHandle(Handle(docAlias));
  605.         docAlias := nil;
  606.  
  607.         (* Put the descriptor list of documents into the odoc or pdoc event *)
  608.         error := AEPutParamDesc(applMessage, keyDirectObject, docDescList);(*◊*)
  609.         if error <> noErr then
  610.             RecoverError(error);
  611.  
  612.         (* The descriptor list is copied into the odoc event, so get rid of it *)
  613.         error := AEDisposeDesc(docDescList);
  614.  
  615.         (* Convert the odoc event to a descriptor suitable for the launch PB *)
  616.         error := AECoerceDesc(applMessage, typeAppParameters, launchDesc); (*<*)
  617.         if error <> noErr then
  618.             RecoverError(error);
  619.  
  620.         (* We’re really interested in launchDesc, so don’t need odoc message *)
  621.         error := AEDisposeDesc(applMessage);
  622.  
  623.         CreateDocListDesc := noErr
  624.     end;
  625.  
  626.  
  627. {$S ProcessUtils}
  628. (*******************************************************************************}
  629. {* Private: SendOpenAppEvent - Send an 'oapp' event to an application}
  630. {*}
  631. {* When an application that is high-level event aware (as indicated by the SIZE}
  632. {* resource flag), it won’t open an untitled document at launch until it gets an}
  633. {* 'oapp' AppleEvent.  This routine sends an 'oapp' event to the application}
  634. {* specified by the processNum parameter.}
  635. {*}
  636. {* First, a descriptor is created that contains the process serial number of the}
  637. {* target application.  This descriptor is then added to the 'oapp' AppleEvent.}
  638. {* Finally, this AppleEvent is sent to the target application which should open}
  639. {* an untitled document in response.}
  640. {*}
  641. {* This routine isn’t called if the application is to open specific documents}
  642. {* when it’s launched.}
  643. {*}
  644. {* If an error occurs, the error code is returned.}
  645. {*******************************************************************************)
  646.  
  647.     function SendOpenAppEvent (processNum: ProcessSerialNumber): OSErr;
  648.  
  649.         var
  650.             theBaby: AEAddressDesc; {PSN desc. of process that’s been opened}
  651.             openEvent: AppleEvent;    {'oapp' AppleEvent}
  652.             reply: AppleEvent;    {Reply from receiving application; ignored}
  653.             error: OSErr;
  654.  
  655.         procedure RecoverError (errorCode: Integer);
  656.  
  657.         begin
  658.             if theBaby.dataHandle <> nil then
  659.                 error := AEDisposeDesc(theBaby);(*◊*)
  660.             if openEvent.dataHandle <> nil then
  661.                 error := AEDisposeDesc(openEvent);(*◊*)
  662.             if reply.dataHandle <> nil then
  663.                 error := AEDisposeDesc(reply);(*◊*)
  664.             SendOpenAppEvent := errorCode;
  665.             EXIT(SendOpenAppEvent)
  666.         end;
  667.  
  668.     begin
  669.         theBaby.dataHandle := nil;
  670.         openEvent.dataHandle := nil;
  671.         reply.dataHandle := nil;
  672.  
  673.         (* Create the Process Serial Number event descriptor *)
  674.         error := AECreateDesc(typeProcessSerialNumber, Ptr(@processNum), SIZEOF(ProcessSerialNumber), theBaby); (*<*)
  675.         if error <> noErr then
  676.             RecoverError(error);
  677.  
  678.         (* Create 'oapp' event with the specified process serial number *)
  679.         error := AECreateAppleEvent(kCoreEventClass, kAEOpenApplication, theBaby, kAutoGenerateReturnID, kAnyTransactionID, openEvent); (*<*)
  680.         if error <> noErr then
  681.             RecoverError(error);
  682.  
  683.         (* Send the 'oapp' event *)
  684.         error := AESend(openEvent, reply, kAENoReply, kAENormalPriority, 0, nil, nil); (*<*)
  685.         if error <> noErr then
  686.             RecoverError(error);
  687.  
  688.         (* Dispose of the descriptor and event *)
  689.         error := AEDisposeDesc(theBaby);(*◊*)
  690.         error := AEDisposeDesc(openEvent);(*◊*)
  691.         if reply.dataHandle <> nil then
  692.             error := AEDisposeDesc(reply)(*◊*)
  693.     end;
  694.  
  695.  
  696. {$S ProcessUtils}
  697. (*******************************************************************************}
  698. {* Private: LaunchProcApp - Launch an application}
  699. {*}
  700. {* This routine saves a bit of work when launching an application because it sets}
  701. {* up the launch parameter block and manages the construction of the application}
  702. {* list descriptor.  The application to open is specified in processFile.  If}
  703. {* there are any documents for the application to open when it’s launched, then}
  704. {* the document list is passed in docList.  If there aren’t any documents for the}
  705. {* application to open, then docList is NIL.  The options parameter specifies the}
  706. {* options to use when launching.  This contains the values for the LaunchFlags}
  707. {* data type specified in the Process Manager chapter of Inside Macintosh VI.}
  708. {*}
  709. {* The process serial number of the specified process is returned in the}
  710. {* processNum parameter.  The function result and the launchError parameter both}
  711. {* return error codes.  One error is returned as a function result.  These kinds}
  712. {* of errors are generated by any call that occurs during the execution of}
  713. {* LaunchProcess that is only used to manage the launching of the specified}
  714. {* application rather than launching the application itself.   The launchError}
  715. {* parameter returns the error code of any error that occurs when the application}
  716. {* is actually launched.}
  717. {*******************************************************************************)
  718.  
  719.     function LaunchProcApp (processFile: FSSpec; docList: DocListHnd; options: LaunchFlags; var processNum: ProcessSerialNumber; var launchError: OSErr): OSErr;
  720.  
  721.         var
  722.             launchParms: LaunchParamBlockRec; {Parameters for launching a file}
  723.             launchDesc: AEDesc;              {Document file list descriptor}
  724.             appParms: AppParametersPtr;    {Pointer to the app parameters}
  725.             error: OSErr;
  726.  
  727.         procedure RecoverError (errorCode: OSErr);
  728.  
  729.         begin
  730.             if launchDesc.dataHandle <> nil then
  731.                 begin
  732.                     HUnlock(Handle(launchDesc.dataHandle));
  733.                     error := AEDisposeDesc(launchDesc)
  734.                 end;
  735.             LaunchProcApp := errorCode;
  736.             EXIT(LaunchProcApp)
  737.         end;
  738.  
  739.     begin
  740.         launchError := noErr;
  741.         launchDesc.dataHandle := nil;
  742.  
  743.         (* Create the document list descriptor, if there’s a document list *)
  744.         if docList <> nil then
  745.             begin
  746.                 error := CreateDocListDesc(docList, launchDesc); (*<*)
  747.                 if error <> noErr then
  748.                     RecoverError(error);
  749.                 HLock(Handle(launchDesc.dataHandle));
  750.  
  751.                 (* Descriptor dataHandle in format suitable for LaunchApplication *)
  752.                 appParms := AppParametersPtr(launchDesc.dataHandle^)
  753.             end
  754.         else
  755.             appParms := nil;
  756.  
  757.         (* Set up the launch parameters *)
  758.         with launchParms do
  759.             begin
  760.                 (*WITH*)
  761.                 launchBlockID := extendedBlock;
  762.                 (*WITH*)
  763.                 launchEPBLength := extendedBlockLen;
  764.                 (*WITH*)
  765.                 launchFileFlags := 0;
  766.                 (*WITH*)
  767.                 launchControlFlags := options;
  768.                 (*WITH*)
  769.                 launchAppSpec := @processFile;
  770.                 (*WITH*)
  771.                 launchAppParameters := appParms
  772.             end;
  773.  
  774.         (* Launch the process *)
  775.         launchError := LaunchApplication(@launchParms);
  776.  
  777.         (* Get rid of the document descriptor list, if there was one *)
  778.         if docList <> nil then
  779.             begin
  780.                 HUnlock(Handle(launchDesc.dataHandle));
  781.                 error := AEDisposeDesc(launchDesc)
  782.             end
  783.         else
  784.             (* No document descriptor list, so send oapp AppleEvent *)
  785.             if launchError = noErr then
  786.                 error := SendOpenAppEvent(launchParms.launchProcessSN);
  787.  
  788.         (* If the launch was successful, return the PSN of the process *)
  789.         if launchError = noErr then
  790.             processNum := launchParms.launchProcessSN;
  791.  
  792.         LaunchProcApp := noErr
  793.     end;
  794.  
  795.  
  796. {$S ProcessUtils}
  797. (*******************************************************************************}
  798. {* Private: GetFirstDAName - Get name of first }
  799. {*}
  800. {* GetFirstDAName gets the name of the first desk accessory in the file specified}
  801. {* by daFile.  This name is returned in the daName parameter.  GetFirstDAName}
  802. {* returns TRUE if a desk accessory was found in the file.  If no desk accessory}
  803. {* could be found, then GetFirstDAName returns FALSE and the daName parameter is}
  804. {* unchanged.}
  805. {*}
  806. {* Desk accessories are stored in DRVR resources, but device drivers are also}
  807. {* stored in DRVR resources.  GetFirstDAName will not return the names of device}
  808. {* drivers.  How do you tell whether a DRVR resource is a desk accessory or a}
  809. {* device driver?  Simply check on the first character of the DRVR resource’s}
  810. {* name.  If it’s a null character, then you’ve got a desk accessory.  If it’s}
  811. {* any other character, then you’ve got a device driver.}
  812. {*******************************************************************************)
  813.  
  814.     function GetFirstDAName (daFile: FSSpec; var daName: Str255): Boolean;
  815.  
  816.         var
  817.             currRF: Integer; {Ref num of current resource file}
  818.             daRF: Integer; {Ref num of DA’s resource file}
  819.             drvrRsrc: Handle;  {Handle to a DRVR resource}
  820.             drvrID: Integer; {ID number of drvrRsrc}
  821.             drvrType: ResType; {Type of resource}
  822.             drvrName: Str255;  {Name of resource}
  823.             numDRVRs: Integer; {Number of DRVR resources in the file}
  824.             index: Integer; {Index into DRVR resources}
  825.             daFound: Boolean; {TRUE if a DA was found in daFile}
  826.  
  827.     begin
  828.         daFound := FALSE;
  829.  
  830.         (* Save the current resource file ref num so we can restore it later *)
  831.         currRF := CurResFile;
  832.  
  833.         (* Open the specified resource file *)
  834.         daRF := FSpOpenResFile(daFile, fsRdPerm);
  835.         if daRF <> -1 then
  836.             begin
  837.                 (* Ready to check each DRVR; only checking, so set ResLoad false *)
  838.                 SetResLoad(FALSE);
  839.                 index := 1;
  840.                 numDRVRs := Count1Resources('DRVR');
  841.  
  842.                 (* Check each DRVR until all checked or a DA was found *)
  843.                 while (index <= numDRVRs) and (not daFound) do
  844.                     begin
  845.                         drvrRsrc := Get1IndResource('DRVR', index);
  846.  
  847.                         (* Get information about the resource *)
  848.                         if drvrRsrc <> nil then
  849.                             begin
  850.                                 GetResInfo(drvrRsrc, drvrID, drvrType, drvrName); (*<*)
  851.  (*<*)
  852.                                         (*<*)
  853.  
  854.                                 (* Find out whether it’s a DA or not *)
  855.                                 if (drvrName[0] > CHR(0)) & (drvrName[1] = CHR(0)) then
  856.                                     daFound := TRUE
  857.                                 else
  858.                                     index := SUCC(index)
  859.                             end
  860.                     end;
  861.  
  862.                 (* Restore the resource file and reset ResLoad *)
  863.                 SetResLoad(TRUE);
  864.                 CloseResFile(daRF)
  865.             end;
  866.  
  867.         (* Get name of first DA in the file, or nil string if none found *)
  868.         if daFound then
  869.             daName := drvrName
  870.         else
  871.             daName[0] := CHR(0);
  872.         GetFirstDAName := daFound;
  873.         UseResFile(currRF)
  874.     end;
  875.  
  876.  
  877. {$S ProcessUtils}
  878. (*******************************************************************************}
  879. {* Public: FindProcess}
  880. {*}
  881. {* The critical routine called by FindProcess is the GetProcessInfo routine.  The}
  882. {* process information it returns is checked against "testFile", and against}
  883. {* "daName" if a desk accessory is being checked.  If they’re equal, then}
  884. {* FindProcess returns TRUE and puts the process information in "testFileInfo".}
  885. {* If the entire Process Manager process list is searched without finding a}
  886. {* match, then FALSE is returned.}
  887. {*******************************************************************************)
  888.  
  889.     function FindProcess (testFile: FSSpec; daName: StringPtr; var testFileInfo: ProcessInfoRec): Boolean;
  890.  
  891.         var
  892.             procInfo: ProcessInfoRec;      {Information on process being checked}
  893.             procNum: ProcessSerialNumber; {Serial num of process being checked}
  894.             procName: Str31;               {Name of open process}
  895.             procSpec: FSSpec;              {File spec of open process’s file}
  896.             procFound: Boolean;             {TRUE if matching DA was found}
  897.             error: OSErr;
  898.  
  899.     begin
  900.         (* Start checking from first process in Process Manager’s list *)
  901.         procNum.highLongOfPSN := 0;
  902.         procNum.lowLongOfPSN := kNoProcess;
  903.  
  904.         (* Loop through entire list of open processes or until match *)
  905.         procFound := FALSE;
  906.         while (not procFound) & (GetNextProcess(procNum) = noErr) do(*◊*)
  907.             begin
  908.                 (* Get information about an open process *)
  909.                 procInfo.processInfoLength := SIZEOF(ProcessInfoRec);
  910.                 procInfo.processName := @procName;
  911.                 procInfo.processAppSpec := @procSpec;
  912.                 error := GetProcessInformation(procNum, procInfo); (*◊*)
  913.  
  914.                 (* Is it the same file as the one we’re testing? *)
  915.                 if error <> noErr then
  916.                     if EqualFSSpec(procInfo.processAppSpec^, testFile) then
  917.                         (* Yes; if it’s an application, we’ve found matching app *)
  918.                         if BAND(procInfo.processMode, modeDeskAccessory) = 0 then
  919.                             procFound := TRUE
  920.                         else
  921.                             (* Is it the same DA as the one we’re testing?}
  922. {                            IF EqualString (procInfo.processName^, daName^,}
  923. {                                    NOT kCaseSens, kDiacSens) THEN}
  924. {                                (* Yes, we’ve found the matching DA *)
  925.                             procFound := TRUE
  926.             end;
  927.  
  928.         if procFound then
  929.             testFileInfo := procInfo;
  930.         FindProcess := procFound
  931.     end;
  932.  
  933.  
  934. {$S ProcessUtils}
  935. (*******************************************************************************}
  936. {* Private: LaunchProcDA - Launch a desk accessory}
  937. {*}
  938. {* This routine launches the desk accessory that’s in the file specified by}
  939. {* process file and with the name specified by daName.  If daName is NIL, then}
  940. {* the first desk accessory in the file, according to Get1IndResource, is}
  941. {* launched.}
  942. {*}
  943. {* As of 7.0b1, LaunchDeskAccessory doesn’t quite work as advertised in Inside}
  944. {* Macintosh VI if the desk accessory that it’s launching is already open.}
  945. {* Inside Macintosh VI says that the open desk accessory that’s being launched is}
  946. {* simply brought to the front.  What I’ve found is that LaunchDeskAccessory}
  947. {* doesn’t do anything but return opWrErr.  If the file that the desk accessory}
  948. {* is in is locked, then a new copy of the desk accessory is opened.  I didn’t}
  949. {* quite like either of those actions, so I explicitly check to see whether the}
  950. {* desk accessory that I’m about to launch is already open.  If it is, then I}
  951. {* call SetFrontProcess myself to bring it to the front.}
  952. {*}
  953. {* To check for this case, I first make sure that I have a name for the desk}
  954. {* accessory.  LaunchDeskAccessory accepts NIL as a desk accessory name if the}
  955. {* caller wants to launch the first desk accessory that the Process Manager finds}
  956. {* in the specified file.  My routine, LaunchProcess, also allows this.  But I}
  957. {* don’t know what the Process Manager thinks is the first desk accessory in the}
  958. {* file.  Since I’m comparing the characteristics of the desk accessory I’m about}
  959. {* to open against the list of open processes, and since I don’t know which desk}
  960. {* accessory is about to be launched if I leave the choice up to the Process}
  961. {* Manager, I just go and get the first desk accessory in the specified file}
  962. {* myself by calling my routine, GetFirstDAName.  Now that I’m guaranteed to have}
  963. {* a name for the desired desk accessory, I don’t have to second-guess the}
  964. {* Process Manager’s choice for the “first” desk accessory in the specified file.}
  965. {*}
  966. {* You can’t tell LaunchDeskAccessory that you want to terminate after the desk}
  967. {* accessory is launched, so I terminate myself by calling ExitToShell if the}
  968. {* terminate parameter was set to TRUE.  You also can’t tell LaunchDeskAccessory}
  969. {* that you want the desk accessory launched into the background.  I still}
  970. {* haven’t thought of a clean way to do this, so for now, I don’t offer that as}
  971. {* an option.  I can think of a few dirty ways.}
  972. {*******************************************************************************)
  973.  
  974.     function LaunchProcDA (processFile: FSSpec; daName: StringPtr; options: LaunchFlags; var processNum: ProcessSerialNumber; var launchError: OSErr): OSErr;
  975.  
  976.         var
  977.             procInfo: ProcessInfoRec;      {Used to search open processes}
  978.             procNum: ProcessSerialNumber; {Process serial num of open process}
  979.             firstDAName: Str255;              {File’s 1st DA name if none spec’d}
  980.             daOpen: Boolean;             {TRUE if spec’d DA is already open}
  981.             error: OSErr;
  982.  
  983.     begin
  984.         error := noErr;
  985.         launchError := noErr;
  986.  
  987.         (* If no DA name specified, get name of first DA in processFile *)
  988.         if daName = nil then
  989.             if GetFirstDAName(processFile, firstDAName) then (*<*)
  990.                 if firstDAName[0] > CHR(0) then
  991.                     daName := @firstDAName
  992.                 else
  993.                     daName := nil;
  994.  
  995.         (* If got name for new DA, see if DA process open w/ same name and file *)
  996.         if daName <> nil then
  997.             daOpen := FindProcess(processFile, daName, procInfo) (*<*)
  998.         else
  999.             daOpen := FALSE;
  1000.  
  1001.         (* Launch the DA to the front, or set it to front if already launched *)
  1002.         if daOpen then
  1003.             error := SetFrontProcess(procInfo.processNumber)
  1004.         else
  1005.             launchError := LaunchDeskAccessory(@processFile, daName);
  1006.  
  1007.         (* If the terminate flag is set, glad to oblige *)
  1008.         if BAND(options, launchContinue) = 0 then
  1009.             ExitToShell;
  1010.  
  1011.         (* Return the process serial number of the DA *)
  1012.         if (error = noErr) and (launchError = noErr) then
  1013.             begin
  1014.                 daOpen := FindProcess(processFile, daName, procInfo); (*<*)
  1015.                 if daOpen then
  1016.                     processNum := procInfo.processNumber
  1017.                 else
  1018.                     begin
  1019.                         procInfo.processNumber.highLongOfPSN := 0;
  1020.                         procInfo.processNumber.lowLongOfPSN := kNoProcess;
  1021.                         processNum := procInfo.processNumber
  1022.                     end
  1023.             end;
  1024.  
  1025.         LaunchProcDA := error
  1026.     end;
  1027.  
  1028.  
  1029. {$S ProcessUtils}
  1030. (*******************************************************************************}
  1031. {* Public: LaunchProcess}
  1032. {*}
  1033. {* The terminate and foreground parmeters are used to set up the flags for}
  1034. {* launching in the launchOptions variable.  The launch parameter block is set up}
  1035. {* with the specified file and launch flags.}
  1036. {*}
  1037. {* If the launchContinue flags is clear, then the LaunchApplication function}
  1038. {* doesn’t return even if it fails.}
  1039. {*******************************************************************************)
  1040.  
  1041.     function LaunchProcess (processFile: FSSpec; daName: StringPtr; docList: DocListHnd; options: LaunchFlags; var returnPSN: ProcessSerialNumber; var launchError: OSErr): OSErr;
  1042.  
  1043.         var
  1044.             fileInfo: FInfo;               {File information for file to launch}
  1045.             newPSN: ProcessSerialNumber; {PSN of launched application}
  1046.             error: OSErr;
  1047.  
  1048.         procedure RecoverError (errorCode: OSErr);
  1049.  
  1050.         begin
  1051.             LaunchProcess := errorCode;
  1052.             EXIT(LaunchProcess)
  1053.         end;
  1054.  
  1055.     begin
  1056.         launchError := noErr;
  1057.  
  1058.         (* Find out whether file to launch is APPL or something else *)
  1059.         error := FSpGetFInfo(processFile, fileInfo); (*<*)
  1060.         if error <> noErr then
  1061.             RecoverError(error);
  1062.  
  1063.         (* Launch the process *)
  1064.         if fileInfo.fdType = 'APPL' then
  1065.             begin
  1066.                 (* File to be launched is an application; launch it *)
  1067.                 error := LaunchProcApp(processFile, docList, options, newPSN, launchError); (*<*)
  1068.                         (*<*)
  1069.                 if error <> noErr then
  1070.                     RecoverError(error);
  1071.             end
  1072.         else
  1073.             begin
  1074.                 (* File to be launch is a desk accessory; launch it *)
  1075.                 error := LaunchProcDA(processFile, daName, options, newPSN, launchError); (*<*)
  1076.                         (*<*)
  1077.                 if error <> noErr then
  1078.                     RecoverError(error)
  1079.             end;
  1080.  
  1081.         (* Return the PSN of the new process *)
  1082.         if launchError = noErr then
  1083.             returnPSN := newPSN;
  1084.  
  1085.         LaunchProcess := noErr
  1086.     end;
  1087.  
  1088.  
  1089. {$S ProcessUtils}
  1090. (*******************************************************************************}
  1091. {* Public: CountProcesses}
  1092. {*}
  1093. {* It’s a pretty simple and straightforward algorithm.}
  1094. {*******************************************************************************)
  1095.  
  1096.     function CountProcesses: Integer;
  1097.  
  1098.         var
  1099.             procNum: ProcessSerialNumber; {Serial num of process being checked}
  1100.             procCount: Integer;             {Number of processes found}
  1101.  
  1102.     begin
  1103.         (* Start checking from first process in Process Manager’s list *)
  1104.         procNum.highLongOfPSN := 0;
  1105.         procNum.lowLongOfPSN := kNoProcess;
  1106.  
  1107.         (* Loop through entire list of open processes *)
  1108.         procCount := 0;
  1109.         while GetNextProcess(procNum) = noErr do(*◊*)
  1110.             procCount := SUCC(procCount);
  1111.  
  1112.         (* Return the number of processes found *)
  1113.         CountProcesses := procCount
  1114.     end;
  1115.  
  1116.  
  1117. {$S ProcessUtils}
  1118. (*******************************************************************************}
  1119. {* Public: TerminateProcess}
  1120. {*}
  1121. {* Terminating a process is done by sending a 'quit' AppleEvent to the process}
  1122. {* whose process serial number is given by "theProcessNum".}
  1123. {*******************************************************************************)
  1124.  
  1125.     function TerminateProcess (theProcessNum: ProcessSerialNumber): OSErr;
  1126.  
  1127.         var
  1128.             theDoomed: AEAddressDesc; {PSN descriptor of process to be terminated}
  1129.             quitEvent: AppleEvent;    {'quit' AppleEvent}
  1130.             reply: AppleEvent;    {Reply from receiving application; ignored}
  1131.             error: OSErr;
  1132.  
  1133.         procedure RecoverError (error: Integer);
  1134.  
  1135.             var
  1136.                 result: OSErr;
  1137.  
  1138.         begin
  1139.             if theDoomed.dataHandle <> nil then
  1140.                 result := AEDisposeDesc(theDoomed);(*◊*)
  1141.             if quitEvent.dataHandle <> nil then
  1142.                 result := AEDisposeDesc(quitEvent);(*◊*)
  1143.             TerminateProcess := error;
  1144.             EXIT(TerminateProcess)
  1145.         end;
  1146.  
  1147.     begin
  1148.         theDoomed.dataHandle := nil;
  1149.         quitEvent.dataHandle := nil;
  1150.         reply.dataHandle := nil;
  1151.  
  1152.         (* Create the Process Serial Number event descriptor *)
  1153.         error := AECreateDesc(typeProcessSerialNumber, Ptr(@theProcessNum), SIZEOF(theProcessNum), theDoomed); (*<*)
  1154.         if error <> noErr then
  1155.             RecoverError(error);
  1156.  
  1157.         (* Create 'quit' event with the specified process serial number *)
  1158.         error := AECreateAppleEvent(kCoreEventClass, kAEQuitApplication, theDoomed, kAutoGenerateReturnID, kAnyTransactionID, quitEvent); (*<*)
  1159.         if error <> noErr then
  1160.             RecoverError(error);
  1161.  
  1162.         (* Send the 'quit' event *)
  1163.         error := AESend(quitEvent, reply, kAENoReply, kAENormalPriority, kNoTimeOut, nil, nil); (*<*)
  1164.         if error <> noErr then
  1165.             RecoverError(error);
  1166.  
  1167.         (* PSN in the AppleEvent, so can dispose of PSN descriptor *)
  1168.         error := AEDisposeDesc(theDoomed);(*◊*)
  1169.  
  1170.         (* Dispose of the 'quit' AppleEvent *)
  1171.         error := AEDisposeDesc(quitEvent)(*◊*)
  1172.     end;
  1173.  
  1174. end.